﻿using ByteSite.Common;
using Quartz;
using Quartz.Impl;
using Quartz.Impl.Matchers;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Configuration;
using System.Net.Http;
using System.Reflection;
using System.Threading.Tasks;
using System.Web;

namespace ByteSite.Quartz
{
    public class QuartzHelper
    {
        private static string test = "";
        private static object obj = new object();
        private static IScheduler scheduler = null;
        private static Dictionary<string, Assembly> AssemblyDict = new Dictionary<string, Assembly>();

        #region 初始化任务调度对象
        public async static Task<IScheduler> GetScheduler()
        {
            try
            {
                if (scheduler == null)
                {
                    ISchedulerFactory sf = new StdSchedulerFactory();
                    scheduler = await sf.GetScheduler();
                    scheduler.ListenerManager.AddJobListener(new CustomJobListener());
                    //scheduler.ListenerManager.AddTriggerListener(new CustomTriggerListener());

                    test = "success";
                    Common.LoggerHelper.Info("任务调度初始化成功");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error("任务调度初始化失败！", ex);
            }
            return scheduler;
        }
        #endregion

        #region 添加job到调度器
        public static async Task<bool> AddJob(Entitys.QuartzTask model)
        {
            var IsSuccess = false;
            var sche = await GetScheduler();
            //model.SchedName = sche.SchedulerName;
            //验证是否是正确的Cron表达式
            if (CronExpression.IsValidExpression(model.cron))
            {
                JobKey jk = new JobKey(model.id.ToString(), model.group);
                //var scheduRes = sche.Result;
                var IsExist = sche.CheckExists(jk).GetAwaiter().GetResult();
                if (IsExist)
                {
                    Common.LoggerHelper.Error($"Job:{model.id.ToString()}已存在", new Exception($"Job:{model.id.ToString()}已存在"));
                    //throw new Exception($"Job:{model.id.ToString()}已存在");
                    return false;
                }
                if (model.type == "dll")
                {
                    var type = GetClassInfo(model.assembly_name, model.assembly_classname);
                    if (type != null)
                    {
                        if (model.start_time == null)
                        {
                            model.start_time = DateTime.Now;
                        }
                        DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(model.start_time, 1);
                        if (model.end_time == null)
                        {
                            model.end_time = DateTime.Parse("2099/12/31 23:59:59");
                        }
                        DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(model.end_time, 1);
                        IJobDetail job = new JobDetailImpl(model.id.ToString(), model.group, type);

                        ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
                                                     .StartAt(starRunTime)
                                                     .EndAt(endRunTime)
                                                     .WithDescription(model.intro)
                                                     .WithIdentity(model.id.ToString(), model.group)
                                                     .WithCronSchedule(model.cron)
                                                     .Build();


                        sche.ScheduleJob(job, trigger).GetAwaiter().GetResult();
                        Common.LoggerHelper.Info($"Job:{model.id.ToString()}添加完成");
                        IsSuccess = true;
                    }
                }
                else if (model.type == "url")
                {
                    if (model.start_time == null)
                    {
                        model.start_time = DateTime.Now;
                    }
                    DateTimeOffset starRunTime = DateBuilder.NextGivenSecondDate(model.start_time, 1);
                    if (model.end_time == null)
                    {
                        model.end_time = DateTime.Parse("2099/12/31 23:59:59");
                    }
                    DateTimeOffset endRunTime = DateBuilder.NextGivenSecondDate(model.end_time, 1);
                    
                    IJobDetail job = JobBuilder.Create<HttpResultfulJob>().WithIdentity(model.id.ToString(), model.group).Build();

                    ICronTrigger trigger = (ICronTrigger)TriggerBuilder.Create()
                                                 .StartAt(starRunTime)
                                                 .EndAt(endRunTime)
                                                 .WithDescription(model.intro)
                                                 .WithIdentity(model.id.ToString(), model.group)
                                                 .WithCronSchedule(model.cron)
                                                 .Build();


                    sche.ScheduleJob(job, trigger).GetAwaiter().GetResult();
                    Common.LoggerHelper.Info($"Job:{model.id.ToString()}添加完成");
                    IsSuccess = true;
                }
            }
            return IsSuccess;
        }
        #endregion

        #region 暂停指定的job
        public static async Task PauseJob(string jobName, string jobgroup)
        {
            try
            {
                var schedu = GetScheduler();
                JobKey jk = new JobKey(jobName, jobgroup);
                var scheduRes = schedu.Result;
                var IsExist = scheduRes.CheckExists(jk).GetAwaiter().GetResult();
                if (IsExist)
                {
                    //任务存在则暂停任务
                    await scheduRes.PauseJob(jk);
                    Common.LoggerHelper.Info($"任务{jobName}已经暂停");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error($"任务{jobName}暂停出错", ex);
                throw ex;
            }
        }
        #endregion

        #region 获取指定的job
        /// <summary>
        /// 查询一个job
        /// </summary>
        /// <param name="groupName"></param>
        /// <param name="jobName"></param>
        /// <returns></returns>
        public static Task<string> GetJob(string jobName, string jobgroup)
        {
            try
            {
                var schedu = GetScheduler();

                Task<ITrigger> trigger = schedu.Result.GetTrigger(new TriggerKey(jobName, jobgroup));
                if (trigger.Result != null)
                {
                    Task<IJobDetail> jobDetail = schedu.Result.GetJobDetail(new JobKey(jobName, jobgroup));
                    if (jobDetail.Result != null)
                    {
                        string json = JsonHelper.ObjectToJSON(new { GroupName = jobgroup, JobName = jobName, DataType = jobDetail.Result.JobType.AssemblyQualifiedName + "," + jobDetail.Result.JobType.FullName });

                        Common.LoggerHelper.Info(json);
                        return Task.FromResult(json);
                    }
                    else
                    {
                        Common.LoggerHelper.Error("该Quartz-Job不存在");
                        return Task.FromResult("该Quartz-Job不存在");
                    }
                }
                else
                {
                    Common.LoggerHelper.Error("该Quartz-Job的触发器不存在");
                    return Task.FromResult("该Quartz-Job的触发器不存在");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error("查询任务调度不存在" + ex.Message);
                return Task.FromResult("查询任务调度不存在" + ex.Message);
            }
        }
        #endregion

        #region 恢复停止的job
        public static async Task ResumeJob(string jobName, string jobgroup)
        {
            try
            {
                var schedu = GetScheduler();
                JobKey jk = new JobKey(jobName, jobgroup);
                var scheduRes = schedu.Result;
                var IsExist = scheduRes.CheckExists(jk).GetAwaiter().GetResult();
                if (IsExist)
                {
                    await scheduRes.ResumeJob(jk);
                    Common.LoggerHelper.Info($"任务{jobName}已经恢复");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error($"任务{jobName}恢复出错", ex);
                throw ex;
            }
        }
        #endregion

        #region 开始指定的job(立即开始，即使没到预定的时间)
        public static async Task StartJob(string jobName, string jobgroup)
        {
            try
            {
                var schedu = GetScheduler();
                JobKey jk = new JobKey(jobName, jobgroup);
                var scheduRes = schedu.Result;
                var IsExist = scheduRes.CheckExists(jk).GetAwaiter().GetResult();
                if (IsExist)
                {
                    await scheduRes.TriggerJob(jk);
                    Common.LoggerHelper.Info($"任务{jobName}已经开始");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error($"任务{jobName}开始失败", ex);
                throw ex;
            }
        }
        #endregion

        #region 删除指定job
        public static async Task DeleteJob(string jobName, string jobgroup)
        {
            try
            {
                var schedu = GetScheduler();
                JobKey jk = new JobKey(jobName, jobgroup);
                var scheduRes = schedu.Result;
                var IsExist = scheduRes.CheckExists(jk).GetAwaiter().GetResult();
                if (IsExist)
                {
                    await scheduRes.DeleteJob(jk);
                    Common.LoggerHelper.Info($"任务{jobName}已经删除");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error($"任务{jobName}删除失败", ex);
                throw ex;
            }
        }
        #endregion

        #region 私有方法 
        /// <summary>
        /// 通过反射获取job的Type
        /// </summary>
        /// <param name="jobName">job的名称</param>
        /// <returns></returns>
        private static Type GetClassInfo(string assemblyName, string className)
        {
            try
            {
                if (HttpContext.Current != null)
                {
                    assemblyName = Common.Utils.GetMapPath("~/bin/" + assemblyName + ".dll");
                }
                else
                {
                    assemblyName = Common.Utils.GetMapPath(assemblyName + ".dll");
                }
                Assembly assembly = null;
                if (!AssemblyDict.TryGetValue(assemblyName, out assembly))
                {
                    assembly = Assembly.LoadFrom(assemblyName);
                    AssemblyDict[assemblyName] = assembly;
                }
                Type type = assembly.GetType(className, true, true);
                if (typeof(IJob).IsAssignableFrom(type))
                {
                    return type;
                }
                else
                {
                    Common.LoggerHelper.Error(string.Format("{0}没继承IJob接口", className), new Exception());
                    return null;
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error("反射出错", ex);
                throw ex;
            }
        }
        #endregion

        public async Task Start()
        {
            try
            {
                var sch = GetScheduler().GetAwaiter().GetResult();
                if (!sch.IsStarted)
                {
                    var allQuertz = JobList.GetAll;
                    if (allQuertz != null && allQuertz.Count > 0)
                    {
                        foreach (var item in allQuertz)
                        {
                            try
                            {
                                //依据配置文件，遍历执行任务 
                                await AddJob(item);
                            }
                            catch (Exception e)
                            {
                                Common.LoggerHelper.Error(e.ToString());
                            }
                        }
                    }

                    await sch.Start();
                    Common.LoggerHelper.Info("任务调度开始");
                }
                var groupNames = sch.GetJobGroupNames().GetAwaiter().GetResult();
                foreach (var item in groupNames)
                {
                    Common.LoggerHelper.Info(item);
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error("任务调度开始失败", ex);
            }
        }

        public void Stop()
        {
            try
            {
                if (!scheduler.IsShutdown)
                {
                    scheduler.Shutdown(true);
                    Common.LoggerHelper.Info("任务调度停止！");
                }
            }
            catch (Exception ex)
            {
                Common.LoggerHelper.Error("任务调度停止失败", ex);
            }
        }
    }
}
